Storage Qualifiers

  • Local variables can only use the const  storage qualifier (or use no storage qualifier).

  • Note that function parameters can use const , in , and out  qualifiers, but as parameter qualifiers .

  • Function return types and structure members do not use storage qualifiers.

  • Initializers in global declarations may only be used in declarations of global variables with no storage qualifier, with a const  qualifier, or with a uniform  qualifier. Global variables without storage qualifiers that are not initialized in their declaration or by the application will not be initialized, but rather will enter main()  with undefined values.

<none: default>
  • If no qualifier is present on a global variable, then the variable has no linkage to the application or shaders running on other pipeline stages.

  • For either global or local unqualified variables, the declaration will appear to allocate memory associated with the processor it targets.

  • This variable will provide read/write access to this allocated memory.

const
  • a variable whose value cannot be changed.

  • Named compile-time constants or read-only variables can be declared using the const  qualifier. The const  qualifier can be used with any of the non-void transparent basic data types, as well as with structures and arrays of these. It is a compile-time error to write to a const  variable outside of its declaration, so they must be initialized when declared. For example,

const vec3 zAxis = vec3 (0.0, 0.0, 1.0);
const float ceiling = a + b; // a and b not necessarily constants
  • Structure members may not be qualified with const . Structure variables can be declared as const , and initialized with a structure constructor or initializer.

  • Initializers for const  declarations at global scope must be constant expressions

  • Constant Expressions :

    • SPIR-V specialization constants are expressed in GLSL as const  with the layout qualifier constant_id .

    • A constant expression  is one of

      • A literal value (e.g. 5  or true ).

      • A variable declared with the const  qualifier and an initializer, where the initializer is a constant expression. This includes both const  declared with a specialization-constant layout qualifier, e.g. layout ( constant_id  = …​), and those declared without a specialization-constant layout qualifier.

      • Built-in variables qualified as const .

      • An expression formed by an operator on operands that are all constant expressions, including getting an element of a constant array, or a member of a constant structure, or components of a constant vector. However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators ( = , += , …​ ) are not included in the operators that can create a constant expression. Also, an array access with a specialization constant as an index does not result in a constant expression.

      • Valid use of the length () method on an explicitly sized object, whether or not the object itself is constant (implicitly sized or run-time sized arrays do not return a constant expression).

      • A constructor whose arguments are all constant expressions.

      • For non-specialization constants only: The value returned by certain built-in function calls whose arguments are all constant expressions, including at least the list below. Any other built-in function that does not access memory (not the texture lookup functions, image access, atomic counter, etc.), that has a non- void  return type, that has no out  parameter, and is not a noise function might also be considered a constant. When a function is called with an argument that is a specialization constant, the result is not a constant expression.

        • Angle and Trigonometric Functions

          • radians

          • degrees

          • sin

          • cos

          • asin

          • acos

        • Exponential Functions

          • pow

          • exp

          • log

          • exp2

          • log2

          • sqrt

          • inversesqrt

        • Common Functions

          • abs

          • sign

          • floor

          • trunc

          • round

          • ceil

          • mod

          • min

          • max

          • clamp

        • Geometric Functions

          • length

          • dot

          • normalize

      • Function calls to user-defined functions (non-built-in functions) cannot be used to form constant expressions.

    • A constant integral expression  is a constant expression that evaluates to a scalar signed or unsigned integer.

    • Constant expressions will be evaluated in an invariant way so as to create the same value in multiple shaders when the same constant expressions appear in those shaders.

    • Constant expressions respect the precise  and invariant  qualifiers but will be always be evaluated in an invariant way, independent of the use of such qualification, so as to create the same value in multiple shaders when the same constant expressions appear in those shaders.

    • Constant-expressions may be evaluated by a host platform, and are therefore not required to compute the same value that the same expression would evaluate to on the shader execution target. However, the host must use the same or greater precision than the target would use. When the precision qualification cannot be determined, the expression is evaluated at highp .

    • Specialization-constant expressions are never evaluated by the compiler front end, but instead retain the expression’s operations needed to evaluate them later on the host.

in
  • linkage into a shader from a previous stage, variable is copied in.

  • Shader input variables are declared with the in  storage qualifier. They form the input interface between previous stages of the API pipeline and the declaring shader. Input variables must be declared at global scope. Values from the previous pipeline stage are copied into input variables at the beginning of shader execution. It is a compile-time error to write to a variable declared as an input.

  • Only the input variables that are statically read need to be written by the previous stage; it is allowed to have superfluous declarations of input variables. This is shown in the following table.

|                                         |                            |                                    |                                            |                 |
|-----------------------------------------|----------------------------|------------------------------------|--------------------------------------------|-----------------|
| Treatment of Mismatched Input Variables |                            | Consuming Shader (input variables) |                                            |                 |
| No Declaration                          | Declared but no Static Use | Declared and Static Use            |                                            |                 |
| Generating Shader (output variables)    | No Declaration             | Allowed                            | Allowed                                    | Link-Time Error |
| Declared but no Static Use              | Allowed                    | Allowed                            | Allowed (values are undefined)             |                 |
| Declared and Static Use                 | Allowed                    | Allowed                            | Allowed (values are potentially undefined) |                 |

  • Consumption errors are based on static use only. Compilation may generate a warning, but not an error, for any dynamic use the compiler can deduce that might cause consumption of undefined values.

  • See “ Built-In Variables ” for a list of the built-in input names.

  • Vertex shader input variables (or attributes) receive per-vertex data. It is a compile-time error to use auxiliary storage or interpolation qualifiers on a vertex shader input. The values copied in are established by the API or through the use of the layout identifier location .

  • It is a compile-time error to declare a vertex shader input with, or that contains, any of the following types:

  • Example declarations in a vertex shader:

in vec4 position;
in vec3 normal;
in vec2 texCoord[4];
  • It is expected that graphics hardware will have a small number of fixed vector locations for passing vertex inputs. Therefore, the OpenGL Shading Language defines each non-matrix input variable as taking up one such vector location. There is an implementation-dependent limit on the number of locations that can be used, and if this is exceeded it will cause a link-time error. (Declared input variables that are not statically used do not count against this limit.) A scalar input counts the same amount against this limit as a vec4 , so applications may want to consider packing groups of four unrelated float inputs together into a vector to better utilize the capabilities of the underlying hardware. A matrix input will use up multiple locations. The number of locations used will equal the number of columns in the matrix.

  • Tessellation control, evaluation, and geometry shader input variables get the per-vertex values written out by output variables of the same names in the previous active shader stage. For these inputs, centroid  and interpolation qualifiers are allowed, but have no effect. Since tessellation control, tessellation evaluation, and geometry shaders operate on a set of vertices, each input variable (or input block, see Interface Blocks  below) needs to be declared as an array. For example,

in float foo[]; // geometry shader input for vertex "out float foo"
  • Each element of such an array corresponds to one vertex of the primitive being processed. Each array can optionally have a size declared. For geometry shaders, the array size will be set by, (or if provided must be consistent with) the input layout  declaration(s) establishing the type of input primitive, as described later in “ Input Layout Qualifiers ”.

  • Some inputs and outputs are arrayed , meaning that for an interface between two shader stages either the input or output declaration requires an extra level of array indexing for the declarations to match. For example, with the interface between a vertex shader and a geometry shader, vertex shader output variables and geometry shader input variables of the same name must have matching types, except that the geometry shader will have one more array dimension than the vertex shader, to allow for vertex indexing. If such an arrayed interface variable is not declared with the necessary additional input or output array dimension, a link-time error will result. Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation evaluation inputs all have an additional level of arrayness relative to other shader inputs and outputs. These inputs and outputs are known as per-vertex-arrayed  inputs and outputs. Component limits for arrayed interfaces (e.g. gl_MaxTessControlInputComponents ) are limits per vertex, not limits for the entire interface.

  • For non-arrayed interfaces (meaning array dimensionally stays the same between stages), it is a link-time error if the input variable is not declared with the same type, including array dimensionality, as the matching output variable.

  • The link-time type-matching rules apply to all declared input and output variables, whether or not they are used.

  • Additionally, tessellation evaluation shaders support per-patch input variables declared with the patch  and in  qualifiers. Per-patch input variables are filled with the values of per-patch output variables written by the tessellation control shader. Per-patch inputs may be declared as one-dimensional arrays, but are not indexed by vertex number. Applying the patch  qualifier to inputs can only be done in tessellation evaluation shaders. As with other input variables, per-patch inputs must be declared using the same type and qualification as per-patch outputs from the previous (tessellation control) shader stage. It is a compile-time error to use patch  with inputs in any other stage.

  • It is a compile-time error to declare a tessellation control, tessellation evaluation or geometry shader input with, or that contains, any of the following types:

  • Fragment shader inputs get per-fragment values, typically interpolated from a previous stage’s outputs. The auxiliary storage qualifiers centroid  and sample  can also be applied, as well as the interpolation qualifiers flat , noperspective , and smooth.

  • It is a compile-time error to declare a fragment shader input with, or that contains, any of the following types:

  • Fragment shader inputs that are, or contain, integral or double-precision floating-point types must be qualified with the interpolation qualifier flat .

  • Fragment inputs are declared as in the following examples:

in vec3 normal;
centroid in vec2 TexCoord;
noperspective in float temperature;
flat in vec3 myColor;
noperspective centroid in vec2 myTexCoord;
  • The fragment shader inputs form an interface with the last active shader in the vertex processing pipeline. For this interface, the last active shader stage output variables and fragment shader input variables of the same name must match in type and qualification, with a few exceptions: The storage qualifiers must, of course, differ (one is in  and one is out ). Also, interpolation qualification (e.g. flat ) and auxiliary qualification (e.g. centroid ) may differ. These mismatches are allowed between any pair of stages. When interpolation or auxiliary qualifiers do not match, those provided in the fragment shader supersede those provided in previous stages. If any such qualifiers are completely missing in the fragment shaders, then the default is used, rather than any qualifiers that may have been declared in previous stages. That is, what matters is what is declared in the fragment shaders, not what is declared in shaders in previous stages.

  • When an interface between shader stages is formed using shaders from two separate program objects, it is not possible to detect mismatches between inputs and outputs when the programs are linked. When there are mismatches between inputs and outputs on such interfaces, the values passed across the interface will be partially or completely undefined.

  • Shaders can ensure matches across such interfaces either by using input and output layout qualifiers (sections “ Input Layout Qualifiers ” and “ Output Layout Qualifiers ”) or by using identical input and output declarations of blocks or variables. Complete rules for interface matching are found in section 7.4.1 “Shader Interface Matching” of the OpenGL Specification .

  • Compute shaders do not permit user-defined input variables and do not form a formal interface with any other shader stage. See “ Compute Shader Special Variables ” for a description of built-in compute shader input variables. All other input to a compute shader is retrieved explicitly through image loads, texture fetches, loads from uniforms or uniform buffers, or other user supplied code. Redeclaration of built-in input variables in compute shaders is not permitted.

out
  • linkage out of a shader to a subsequent stage, variable is copied out.

  • Shader output variables are declared with the out  storage qualifier. They form the output interface between the declaring shader and the subsequent stages of the API pipeline. Output variables must be declared at global scope. During shader execution they will behave as normal unqualified global variables. Their values are copied out to the subsequent pipeline stage on shader exit. Only output variables that are read by the subsequent pipeline stage need to be written; it is allowed to have superfluous declarations of output variables.

  • There is not  an inout  storage qualifier for declaring a single variable name as both input and output to a shader. Also, a variable cannot be declared with both the in  and the out  qualifiers, this will result in a compile-time or link-time error. Output variables must be declared with different names than input variables. However, nesting an input or output inside an interface block with an instance name allows the same names with one referenced through a block instance name.

  • Vertex, tessellation evaluation, and geometry output variables output per-vertex data and are declared using the out  storage qualifier. Applying patch  to an output can only be done in a tessellation control shader. It is a compile-time error to use patch  on outputs in any other stage.

  • It is a compile-time error to declare a vertex, tessellation evaluation, tessellation control, or geometry shader output with, or that contains, any of the following types:

  • Individual outputs are declared as in the following examples:

out vec3 normal;
centroid out vec2 TexCoord;
invariant centroid out vec4 Color;
flat out vec3 myColor;
sample out vec4 perSampleColor;
  • These can also appear in interface blocks, as described in “ Interface Blocks ”. Interface blocks allow simpler addition of arrays to the interface from vertex to geometry shader. They also allow a fragment shader to have the same input interface as a geometry shader for a given vertex shader.

  • Tessellation control shader output variables are used to output per-vertex and per-patch data. Per-vertex output variables are arrayed (see arrayed  under “ Input Variables ”) and declared using the out  qualifier without the patch  qualifier. Per-patch output variables are declared using the patch  and out  qualifiers.

  • Since tessellation control shaders produce an arrayed primitive comprising multiple vertices, each per-vertex output variable (or output block, see Interface Blocks  below) needs to be declared as an array. For example,

out float foo[]; // feeds next stage input "in float foo[]"
  • Each element of such an array corresponds to one vertex of the primitive being produced. Each array can optionally have a size declared. The array size will be set by (or if provided must be consistent with) the output layout declaration(s) establishing the number of vertices in the output patch, as described later in “ Tessellation Control Outputs ”.

  • Each tessellation control shader invocation has a corresponding output patch vertex, and may assign values to per-vertex outputs only if they belong to that corresponding vertex. If a per-vertex output variable is used as an l-value, it is a compile-time or link-time error if the expression indicating the vertex index is not the identifier gl_InvocationID .

  • The order of execution of a tessellation control shader invocation relative to the other invocations for the same input patch is undefined unless the built-in function barrier () is used. This provides some control over relative execution order. When a shader invocation calls barrier (), its execution pauses until all other invocations have reached the same point of execution. Output variable assignments performed by any invocation executed prior to calling barrier () will be visible to any other invocation after the call to barrier () returns.

  • Because tessellation control shader invocations execute in undefined order between barriers, the values of per-vertex or per-patch output variables will sometimes be undefined. Consider the beginning and end of shader execution and each call to barrier () as synchronization points. The value of an output variable will be undefined in any of the three following cases:

    1. At the beginning of execution.

    2. At each synchronization point, unless

      • the value was well-defined after the previous synchronization point and was not written by any invocation since, or

      • the value was written by exactly one shader invocation since the previous synchronization point, or

      • the value was written by multiple shader invocations since the previous synchronization point, and the last write performed by all such invocations wrote the same value.

    3. When read by a shader invocation, if

      • the value was undefined at the previous synchronization point and has not been written by the same shader invocation since, or

      • the output variable is written to by any other shader invocation between the previous and next synchronization points, even if that assignment occurs in code following the read.

  • Fragment outputs output per-fragment data and are declared using the out  storage qualifier. It is a compile-time error to use auxiliary storage qualifiers or interpolation qualifiers in a fragment shader output declaration. It is a compile-time error to declare a fragment shader output with, or that contains, any of the following types:

  • Fragment outputs are declared as in the following examples:

out vec4 FragmentColor;
out uint Luminosity;
  • Compute shaders have no built-in output variables, do not support user-defined output variables and do not form a formal interface with any other shader stage. All outputs from a compute shader take the form of the side effects such as image stores and operations on atomic counters.

attribute
  • compatibility profile only and vertex language only; same as in when in a vertex shader

uniform
  • value does not change across the primitive being processed, uniforms form the linkage between a shader, API, and the application.

  • The uniform  qualifier is used to declare global variables whose values are the same across the entire primitive being processed. All uniform  variables are read-only and are initialized externally either at link time or through the API. The link-time initial value is either the value of the variable’s initializer, if present, or 0 if no initializer is present. Opaque types cannot have initializers, or a compile-time error results. When targeting Vulkan, it is a compile-time error to declare uniform  variables outside a block.

  • Example declarations are:

uniform vec4 lightPosition;
uniform vec3 color = vec3(0.7, 0.7, 0.2); // value assigned at link time
  • The uniform  qualifier can be used with any of the basic data types, or when declaring a variable whose type is a structure, or an array of any of these.

  • There is an implementation-dependent limit on the amount of storage for uniforms that can be used for each type of shader and if this is exceeded it will cause a compile-time or link-time error. Uniform variables that are declared but not used do not count against this limit. The number of user-defined uniform variables and the number of built-in uniform variables that are used within a shader are added together to determine whether available uniform storage has been exceeded.

  • Uniforms in shaders all share a single global name space when linked into a program or separable program. Hence, the types, initializers, and any location specifiers of all statically used uniform variables with the same name must match across all shaders that are linked into a single program. However it is not required to repeat the initializer or location specifier in all the linked shaders. While this single uniform name space is cross stage, a uniform variable name’s scope is per stage: If a uniform variable name is declared in one stage (e.g. a vertex shader) but not in another (e.g. a fragment shader), then that name is still available in the other stage for a different use.

varying
  • compatibility profile only and vertex and fragment languages only; same as out when in a vertex shader and same as in when in a fragment shader

buffer
  • value is stored in a buffer object, and can be read or written both by shader invocations and the API.

  • The buffer  qualifier is used to declare global variables whose values are stored in the data store of a buffer object bound through the API. Buffer variables can be read and written, with the underlying storage shared among all active shader invocations. Buffer variable memory reads and writes within a single shader invocation are processed in order. However, the order of reads and writes performed in one invocation relative to those performed by another invocation is largely undefined. Buffer variables may be qualified with memory qualifiers affecting how the underlying memory is accessed, as described in “ Memory Qualifiers ”.

  • The buffer  qualifier can be used to declare interface blocks (see “ Interface Blocks ”), which are then referred to as shader storage blocks. It is a compile-time error to declare buffer variables outside a block.

// use buffer to create a buffer block (shader storage block)
buffer BufferName { // externally visible name of buffer
    int count;      // typed, shared memory...
    ...             // ...
    vec4 v[];       // last member may be an array that is not sized
                    // until after link time (dynamically sized)
} Name;             // name of block within the shader
  • There are implementation-dependent limits on the number of shader storage blocks used for each type of shader, the combined number of shader storage blocks used for a program, and the amount of storage required by each individual shader storage block. If any of these limits are exceeded, it will cause a compile-time or link-time error.

  • If multiple shaders are linked together, then they will share a single global buffer variable name space. Hence, the types of all declared buffer variables with the same name must match across all shaders that are linked into a single program.

shared
  • compute shader only; variable storage is shared across all work items in a workgroup.

  • The shared  qualifier is used to declare global variables that have storage shared between all work items in a compute shader workgroup. Variables declared as shared  may only be used in compute shaders (see “ Compute Processor ”). Any other declaration of a shared  variable is a compile-time error. Shared variables are implicitly coherent (see “ Memory Qualifiers ”).

  • Variables declared as shared  may not have initializers and their contents are undefined at the beginning of shader execution. Any data written to shared  variables will be visible to other work items (executing the same shader) within the same workgroup.

  • In the absence of synchronization, the order of reads and writes to the same shared  variable by different invocations of a shader is not defined.

  • In order to achieve ordering with respect to reads and writes to shared  variables, control flow barriers must be employed using the barrier () function (see “ Shader Invocation Control Functions ”).

  • There is a limit to the total size of all variables declared as shared  in a single program. This limit, expressed in units of basic machine units may be determined by using the OpenGL API to query the value of MAX_COMPUTE_SHARED_MEMORY_SIZE.

Auxiliars

  • Some input and output qualified variables can be qualified with at most one additional auxiliary storage qualifier

  • Auxiliary storage qualifiers can only be used with the in  or out  storage qualifiers.

centroid
  • centroid-based interpolation

sample
  • per-sample interpolation

patch
  • per-tessellation-patch attributes

Interface Blocks

  • To implement our constant data we have to use an interface block. Interface blocks in shader code are used to group multiple global variables of the same <storage>  type.

  • In theory they aren’t necessarily solely for constant data.

layout(set = 0, binding = 0) uniform PerMeshData {
    vec4 camera_position;
    mat4 model_matrix;
    vec3 mesh_color;
} per_mesh_data;
  • Interface blocks are still global variables, and technically still follow the global variable format. However, the difference is that they have to be given a user-defined type.

  • They work exactly the same way as a struct  in GLSL/C++. For example, to access the model matrix in this interface block, you’d use per_mesh_data.model_matrix .

  • Input, output, uniform, and buffer variable declarations can be grouped into named interface blocks to provide coarser granularity backing than is achievable with individual declarations. They can have an optional instance name, used in the shader to reference their members. An output block of one programmable stage is backed by a corresponding input block in the subsequent programmable stage. A uniform block  is backed by the application with a buffer object. A buffer block , also known as a shader storage block , is also backed by the application with a buffer object. It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader. These uses are reserved for future use.

  • An interface block declaration is defined in the grammar as follows:

    • interface-block  :

    • type_qualifier   block-name   {   member-list   }   instance-nameopt   ;

    • block-name  :

    • identifier

    • member-list  :

    • member-declaration

    • member-declaration   member-list

    • member-declaration  :

    • layout-qualifieropt   qualifiersopt   type   declarators   ;

    • instance-name  :

    • identifier

    • identifier   array-specifier

  • Each of the above elements is discussed below.

  • First, an example,

uniform Transform {
    mat4 ModelViewMatrix;
    mat4 ModelViewProjectionMatrix;
    uniform mat3 NormalMatrix;      // allowed restatement of qualifier
    float Deformation;
};
  • The above establishes a uniform block named “Transform” with four uniforms grouped inside it.

  • type-qualifier  determines the interface of which the block will be a part and, optionally, additional qualifiers that are applied to the block. It is a compile-time error if it does not include one of the storage qualifiers in , out , uniform  or buffer . It may optionally include layout qualifiers , the auxiliary storage qualifier   patch , and the precise qualifier . buffer  blocks may additionally include Memory Qualifiers . It is a compile-time error to include any other qualifiers.

  • member-list  declares the variables that are to be grouped into the block. Types and declarators are the same as for other input, output, uniform, and buffer variable declarations outside blocks, with these exceptions:

  • Initializers are not allowed

  • Opaque types are not allowed

  • Structure definitions cannot be nested inside a block

  • Any of these would result in a compile-time error.

  • If no optional qualifier is used in a member-declaration, the qualification of the member includes all in , out , patch , uniform , or buffer  as determined by interface-qualifier . If optional qualifiers are used, they can include interpolation qualifiers, auxiliary storage qualifiers, precision qualifiers, and storage qualifiers and they must declare an input, output, or uniform member consistent with the interface qualifier of the block: Input variables, output variables, uniform variables, and buffer  members can only be in in  blocks, out  blocks, uniform  blocks, and shader storage blocks, respectively.

  • Repeating the in , out , patch , uniform , or buffer  interface qualifier for a member’s storage qualifier is optional. For example,

in Material {
    smooth in vec4 Color1; // legal, input inside in block
    smooth vec4 Color2;    // legal, 'in' inherited from 'in Material'
    vec2 TexCoord;         // legal, TexCoord is an input
    uniform float Atten;   // illegal, mismatched storage qualifier
};
  • Members of uniform  or buffer  storage blocks are always represented in memory as highp , regardless of any precision qualifier associated with the declaration. When values are read from or written to such variables they are converted to or from the declared precision as described in Conversion Between Precisions . Operations on the values within the shader will take place using the declared precision as normal.

  • A shader interface  is defined to be one of these:

  • All the uniform variables and uniform blocks declared in a program. This spans all compilation units linked together within one program.

  • All the buffer  blocks declared in a program.

  • The boundary between adjacent programmable pipeline stages: This spans all the outputs declared in all compilation units of the first stage and all the inputs declared in all compilation units of the second stage. Note that for the purposes of this definition, the fragment shader and the preceding shader are considered to have a shared boundary even though in practice, all values passed to the fragment shader first pass through the rasterizer and interpolator.

  • The block name ( block-name ) is used to match within shader interfaces: an output block of one pipeline stage will be matched to an input block with the same name in the subsequent pipeline stage. For uniform or shader storage blocks, the application uses the block name to identify the block. Block names have no other use within a shader beyond interface matching; it is a compile-time error to use a block name at global scope for anything other than as a block name (e.g. use of a block name for a global variable name or function name is currently reserved). It is a compile-time error to use the same block name for more than one block declaration in the same shader interface (as defined above) within one shader, even if the block contents are identical.

  • Matched block names within a shader interface (as defined above) must match in terms of having the same number of declarations with the same sequence of types and the same sequence of member names, as well as having matching member-wise layout qualification (see next section). Matched uniform or shader storage block names (but not input or output block names) must also either all be lacking an instance name or all having an instance name, putting their members at the same scoping level. When instance names are present on matched block names, it is allowed for the instance names to differ; they need not match for the blocks to match. Furthermore, if a matching block is declared as an array, then the array sizes must also match (or follow array matching rules for the shader interface between consecutive shader stages). Any mismatch will generate a link-time error. A block name is allowed to have different definitions in different shader interfaces within the same shader, allowing, for example, an input block and output block to have the same name.

  • If an instance name ( instance-name ) is not used, the names declared inside the block are scoped at the global level and accessed as if they were declared outside the block. If an instance name ( instance-name ) is used, then it puts all the members inside a scope within its own name space, accessed with the field selector ( . ) operator (analogously to structures). For example,

in Light {
    vec4 LightPos;
    vec3 LightColor;
};
in ColoredTexture {
    vec4 Color;
    vec2 TexCoord;
} Material;           // instance name
vec3 Color;           // different Color than Material.Color
vec4 LightPos;        // illegal, already defined
...
... = LightPos;       // accessing LightPos
... = Material.Color; // accessing Color in ColoredTexture block
  • Outside the shading language (i.e., in the API), members are similarly identified except the block name is always used in place of the instance name (API accesses are to shader interfaces, not to shaders). If there is no instance name, then the API does not use the block name to access a member, just the member name.

  • Within a shader interface, all declarations of the same global name must be for the same object and must match in type and in whether they declare a variable or member of a block with no instance name. The API also needs this name to uniquely identify an object in the shader interface. It is a link-time error if any particular shader interface contains

  • two different blocks, each having no instance name, and each having a member of the same name, or

  • a variable outside a block, and a block with no instance name, where the variable has the same name as a member in the block.

out Vertex {
    vec4 Position;  // API transform/feedback will use "Vertex.Position"
    vec2 Texture;
} Coords;           // shader will use "Coords.Position"
out Vertex2 {
    vec4 Color;     // API will use "Color"
    float Color2;
};
// in same program as Vertex2 above:
out Vertex3 {
    float Intensity;
    vec4 Color;     // ERROR, name collision with Color in Vertex2
};
float Color2;       // ERROR, collides with Color2 in Vertex2
  • For blocks declared as arrays, the array index must also be included when accessing members, as in this example

uniform Transform { // API uses "Transform[2]" to refer to instance 2
    mat4 ModelViewMatrix;
    mat4 ModelViewProjectionMatrix;
    vec4 a[]; // array will get implicitly sized
    float Deformation;
} transforms[4];
...
... = transforms[2].ModelViewMatrix; // shader access of instance 2
// API uses "Transform.ModelViewMatrix" to query an offset or other query
transforms[x].a.length(); // same length for 'a' for all x
Transform[x];             // illegal, must use 'transforms'
Transform.a.length();     // illegal, must use 'transforms'
...transforms[2].a[3]...  // if these are the only two dereferences of 'a',
...transforms[3].a[7]...  // then 'a' must be size 8, for all
transforms[x]
  • For uniform or shader storage blocks declared as an array, each individual array element corresponds to a separate buffer object bind range, backing one instance of the block. As the array size indicates the number of buffer objects needed, uniform and shader storage block array declarations must specify an array size. A uniform or shader storage block array can only be indexed with a dynamically uniform integral expression, otherwise results are undefined.

  • When using OpenGL API entry points to identify the name of an individual block in an array of blocks, the name string may include an array index (e.g. Transform[2] ). When using OpenGL API entry points to refer to offsets or other characteristics of a block member, an array index must not be specified (e.g. Transform.ModelViewMatrix ).

  • Tessellation control, tessellation evaluation and geometry shader input blocks must be declared as arrays and follow the array declaration and linking rules for all shader inputs for the respective stages. All other input and output block arrays must specify an array size.

  • There are implementation-dependent limits on the number of uniform blocks and the number of shader storage blocks that can be used per stage. If either limit is exceeded, it will cause a link-time error.